using System;
using System.Xml;
using System.Xml.XPath;
using Framework.Library.Core.Utils.Miscellaneous;

namespace Framework.Library.Core.Utils.Xml
{
    public static class XPathUtils
    {
        private static readonly XPathDocument _emptyXPathDocument;

        static XPathUtils()
        {
            var doc = new XmlDocument();
            doc.AppendChild(doc.CreateElement("DocumentRoot"));

            _emptyXPathDocument = new XPathDocument(new XmlNodeReader(doc));
        }

        public static XPathDocument EmptyXPathDocument
        {
            get { return _emptyXPathDocument; }
        }

        /// <summary>
        /// Gets the text from a selected node.
        /// </summary>
        /// <param name="navigable">The XPath navigable value to select from.</param>
        /// <param name="xpath">The XPath expression to select a text node.</param>
        /// <param name="selectedText">The selected text.</param>
        /// <returns>The text from the selected node.</returns>
        public static bool TrySelectNodeText(IXPathNavigable navigable, string xpath, out string selectedText)
        {
            return MiscellaneousUtils.TryAction(delegate { return SelectNodeText(navigable, xpath); }, out selectedText);
        }

        public static string SelectNodeText(IXPathNavigable navigable, string xpath)
        {
            if (navigable == null)
                throw new ArgumentNullException("navigable");
            if (xpath == null)
                throw new ArgumentNullException("xpath");

            XPathNavigator startNavigator = navigable.CreateNavigator();
            XPathNavigator selectedNodeNavigator = startNavigator.SelectSingleNode(xpath);

            if (selectedNodeNavigator != null)
            {
                if (selectedNodeNavigator.NodeType == XPathNodeType.Text)
                    return selectedNodeNavigator.Value;
                else
                    throw new XmlException(string.Format("XPath expression '{0}' returned a non-text node.", xpath));
            }
            else
            {
                throw new XmlException(string.Format("XPath expression '{0}' did not return a node.", xpath));
            }
        }

        public static bool TrySelectNodeXml(IXPathNavigable navigable, string xpath, out string selectedXml)
        {
            return MiscellaneousUtils.TryAction(delegate { return SelectNodeXml(navigable, xpath); }, out selectedXml);
        }

        public static string SelectNodeXml(IXPathNavigable navigable, string xpath)
        {
            if (navigable == null)
                throw new ArgumentNullException("navigable");
            if (xpath == null)
                throw new ArgumentNullException("xpath");

            XPathNavigator startNavigator = navigable.CreateNavigator();
            XPathNavigator selectedNavigator = startNavigator.SelectSingleNode(xpath);

            if (selectedNavigator != null)
                return selectedNavigator.InnerXml;
            else
                throw new XmlException(string.Format("XPath expression '{0}' did not return a node.", xpath));
        }
    }
}